/*
 * @Author: NingJian
 * @Date: 2021-08-26 16:42:32
 * @LastEditTime: 2022-09-06 09:23:33
 * @LastEditors: NingJian
 * @Description:
 * @FilePath: /artem-muse-firmware/src/muse/muse.c
 *
 */

#include "muse/muse.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <metal/cpu.h>
#include <metal/led.h>
#include <metal/button.h>
#include <metal/switch.h>
#include <metal/timer.h>

struct metal_cpu *cpu;
struct metal_interrupt *cpu_intr, *tmr_intr;
struct metal_interrupt *but0_ic;
int tmr_id, but0_irq;

static const uint32_t wait_signal = 0x00000005;

static struct MUSE_CONTROL_INFO muse_contrl_info;

static struct MUSE_CONTEXT muse_context_array[MUSE_SUPPORT_MAX_NUMBER];

static struct timeval muse_start_time[MUSE_SUPPORT_MAX_NUMBER];

static uint32_t muse_regs_conf_time[MUSE_SUPPORT_MAX_NUMBER];

void muse_uart_set_band(uint32_t value) { RISCV_UART_BAND = value; }

void muse_display_instruction(void) {
  muse_uart_set_band(0x11);
  printf("\n");
  printf("SIFIVE, INC.\n!!\n");
  printf("\n");
  printf("Artem Muse Firmware V1.0.0 \n\n");
  printf("http://www.bjxx.tech/ \n");
  printf("\n");
}

void muse_handle_isr(int id, void *data) {
  int *pcie_int_clr_0x00 = (int *)0x40002000;
  muse_uart_set_band(0x11);
  printf("muse_handle_isr,Interrupt ID is:%d.\n", id);
  if (id == 16) {
    printf("Interrupt Reg is:%x.\n", RISCV_ISR_REG);
    printf("Clear Muse Calculation Done Interrupt\n");
    // MUSE_REG_INTERRUPT_STATE = 0xffffffff;
  } else {
    printf("Clear Pcie Interrupt\n");
    *pcie_int_clr_0x00 = 0x01;
  }
}

int muse_interrupt_test(void) {
  int rc;
  struct metal_button *but0, *but1, *but2, *but3;
  struct metal_switch *swch3;

  // Lets get the CPU and and its interrupt
  cpu = metal_cpu_get(metal_cpu_get_current_hartid());
  if (cpu == NULL) {
    printf("CPU null.\n");
    return 2;
  }
  cpu_intr = metal_cpu_interrupt_controller(cpu);
  if (cpu_intr == NULL) {
    printf("CPU interrupt controller is null.\n");
    return 3;
  }
  metal_interrupt_init(cpu_intr);
#if 1
  // Setup Timer and its interrupt
  tmr_intr = metal_cpu_timer_interrupt_controller(cpu);
  if (tmr_intr == NULL) {
    printf("TIMER interrupt controller is  null.\n");
    return 4;
  }
  metal_interrupt_init(tmr_intr);
  tmr_id = metal_cpu_timer_get_interrupt_id(cpu);
#endif
  // Setup Buttons 0-3 and its interrupt
  but0 = metal_button_get("BTN0");
  but0_ic = metal_button_interrupt_controller(but0);
  if (but0_ic == NULL) {
    printf("BTN0 interrupt controller is null.\n");
    return 4;
  }
  metal_interrupt_init(but0_ic);
  // but0_irq = metal_button_get_interrupt_id(but0);
  for (but0_irq = 17; but0_irq <= 172; but0_irq++) {
    rc = metal_interrupt_register_handler(but0_ic, but0_irq, muse_handle_isr,
                                          NULL);
    if (rc < 0) {
      printf("Interrupt but0_irq=%d registration failed\n", but0_irq);
      return (rc * -1);
    }
    // Lets enable the Buttons and Switch interrupts
    if (metal_interrupt_enable(but0_ic, but0_irq) == -1) {
      printf("Interrupt enable failed\n");
      return 5;
    }
  }

  // Lastly CPU interrupt
  if (metal_interrupt_enable(cpu_intr, 0) == -1) {
    printf("CPU Interrupt enable failed\n");
    return 6;
  }

  muse_display_instruction();

  // while(1);

  return 0;
}

void get_muse_ctrl_info() {
  uint8_t *ctrl_ptr = (uint8_t *)DDR_BASE_ADDR;
  memcpy((uint8_t *)&muse_contrl_info, ctrl_ptr, sizeof(muse_contrl_info));
}

void init_muse_ctrl_info_ddr() {
  uint8_t *ctrl_ptr = (uint8_t *)DDR_BASE_ADDR;
  memset(ctrl_ptr, 0x00, sizeof(muse_contrl_info));
}

void get_muse_context_info() {
  int i = 0;
  for (i = 0; i < MUSE_SUPPORT_MAX_NUMBER; ++i) {
    if (muse_contrl_info.muses_work_status[i] != 0x00) {
      uint8_t *ctrl_ptr =
          ((uint8_t *)DDR_BASE_ADDR) + muse_contrl_info.muses_ddr_addr[i];
      memcpy((uint8_t *)&(muse_context_array[i]), ctrl_ptr,
             sizeof(struct MUSE_CONTEXT));

      // show_muse_context(i);
    }
  }
}

void update_muse_context_info(int id) {
  uint8_t *ctrl_ptr =
      (uint8_t *)DDR_BASE_ADDR + muse_contrl_info.muses_ddr_addr[id];
  memcpy(ctrl_ptr, (uint8_t *)&(muse_context_array[id]),
         sizeof(struct MUSE_CONTEXT));
}

void delay_loop(int v) {
  int keep = 0;
  for (int i = 0; i < v * 10000; ++i) {
    ++keep;
  }
}

void set_muse_reg_value(uint32_t muse_base_addr, uint32_t reg_offset,
                        uint32_t value) {
  MUSE_REG_X(muse_base_addr, reg_offset) = value;
  // printf("set_muse_reg_value  reg: 0x%x  value: 0x%x \n", reg_offset, value);
  // delay_loop(10);
}

uint32_t get_muse_reg_value(uint32_t muse_base_addr, uint32_t reg_offset) {
  return MUSE_REG_X(muse_base_addr, reg_offset);
}

void muse_conf_regs(int id) {
  uint32_t muse_base_addr = muse_context_array[id].base_addr;
  struct timeval muse_conf_start_time;
  struct timeval muse_conf_end_time;

  metal_gettimeofday(&muse_conf_start_time, NULL);

  set_muse_reg_value(muse_base_addr, MUSE_REG_INTERRUPT_STATE, 0xffffffff);

  set_muse_reg_value(muse_base_addr, MUSE_REG_ACT_BASE_ADDR0,
                     muse_context_array[id].act_addr_offset);

  set_muse_reg_value(muse_base_addr, MUSE_REG_ACT_BASE_ADDR1,
                     muse_context_array[id].act_addr_offset);

  set_muse_reg_value(muse_base_addr, MUSE_REG_WEIGHT_BASE_ADDR,
                     muse_context_array[id].wet_addr_offset);

  set_muse_reg_value(muse_base_addr, MUSE_REG_OUTPUT_BASE_ADDR,
                     muse_context_array[id].out_addr_offset);

  set_muse_reg_value(muse_base_addr, MUSE_REG_INST_BASE_ADDR,
                     muse_context_array[id].run_layer_inst_addr);

  set_muse_reg_value(muse_base_addr, MUSE_REG_INST_LEN_REAL,
                     muse_context_array[id].run_layer_inst_len_real);

  set_muse_reg_value(muse_base_addr, MUSE_REG_INST_LEN_READ,
                     muse_context_array[id].run_layer_inst_len_read);

  set_muse_reg_value(muse_base_addr, MUSE_REG_CONFIG_END, 0x01);

  metal_gettimeofday(&muse_conf_end_time, NULL);
  muse_regs_conf_time[id] =
      (muse_conf_end_time.tv_sec - muse_conf_start_time.tv_sec) * 1000 * 1000 +
      (muse_conf_end_time.tv_usec - muse_conf_start_time.tv_usec);
  muse_regs_conf_time[id] = muse_regs_conf_time[id] * 8;

#ifdef DEBUG
  printf("MUSE_REG_INTERRUPT_STATE : %d \n",
         MUSE_REG_X(muse_base_addr, MUSE_REG_INTERRUPT_STATE));
  printf("MUSE_REG_ACT_BASE_ADDR0 : %d \n",
         MUSE_REG_X(muse_base_addr, MUSE_REG_ACT_BASE_ADDR0));
  printf("MUSE_REG_ACT_BASE_ADDR1 : %d \n",
         MUSE_REG_X(muse_base_addr, MUSE_REG_ACT_BASE_ADDR1));
  printf("MUSE_REG_WEIGHT_BASE_ADDR : %d \n",
         MUSE_REG_X(muse_base_addr, MUSE_REG_WEIGHT_BASE_ADDR));
  printf("MUSE_REG_OUTPUT_BASE_ADDR : %d \n",
         MUSE_REG_X(muse_base_addr, MUSE_REG_OUTPUT_BASE_ADDR));
  printf("MUSE_REG_INST_BASE_ADDR : %d \n",
         MUSE_REG_X(muse_base_addr, MUSE_REG_INST_BASE_ADDR));
  printf("MUSE_REG_INST_LEN_REAL : %d \n",
         MUSE_REG_X(muse_base_addr, MUSE_REG_INST_LEN_REAL));
  printf("MUSE_REG_INST_LEN_READ : %d \n",
         MUSE_REG_X(muse_base_addr, MUSE_REG_INST_LEN_READ));
  printf("MUSE_REG_CONFIG_END : %d \n",
         MUSE_REG_X(muse_base_addr, MUSE_REG_CONFIG_END));
#endif
}

void muse_read_regs(int id) {
  uint32_t muse_base_addr = muse_context_array[id].base_addr;
  muse_context_array[id].muse_reg_inst_base_addr =
      MUSE_REG_X(muse_base_addr, MUSE_REG_INST_BASE_ADDR);
  muse_context_array[id].muse_reg_act_base_addr0 =
      MUSE_REG_X(muse_base_addr, MUSE_REG_ACT_BASE_ADDR0);
  //   muse_context_array[id].muse_reg_act_base_addr1 =
  //       MUSE_REG_X(muse_base_addr, MUSE_REG_ACT_BASE_ADDR1);
  //记录配置寄存器的时间
  muse_context_array[id].muse_reg_act_base_addr1 = muse_regs_conf_time[id];
  muse_context_array[id].muse_reg_weight_base_addr =
      MUSE_REG_X(muse_base_addr, MUSE_REG_WEIGHT_BASE_ADDR);
  muse_context_array[id].muse_reg_output_base_addr =
      MUSE_REG_X(muse_base_addr, MUSE_REG_OUTPUT_BASE_ADDR);
  muse_context_array[id].muse_reg_inst_len_real =
      MUSE_REG_X(muse_base_addr, MUSE_REG_INST_LEN_REAL);
  muse_context_array[id].muse_reg_inst_len_read =
      MUSE_REG_X(muse_base_addr, MUSE_REG_INST_LEN_READ);
  muse_context_array[id].muse_reg_config_end =
      MUSE_REG_X(muse_base_addr, MUSE_REG_CONFIG_END);
  muse_context_array[id].muse_reg_interrupt_state =
      MUSE_REG_X(muse_base_addr, MUSE_REG_INTERRUPT_STATE);
}

void show_muse_context(int id) {
  printf("=============  MUSE CONTEXT ============= \n");
  printf("muse id %d \n", (int)muse_context_array[id].muse_id);
  printf("muse status %d \n", (int)muse_context_array[id].muse_status);
  printf("muse base addr 0x%x \n", muse_context_array[id].base_addr);
  printf("muse ddr addr 0x%x \n", muse_context_array[id].ddr_addr);
  printf("muse layer number %d \n",
         (int)muse_context_array[id].run_layer_number);
  printf("muse layer inst addr 0x%x \n",
         muse_context_array[id].run_layer_inst_addr);
  printf("muse layer inst read 0x%x \n",
         muse_context_array[id].run_layer_inst_len_read);
  printf("muse layer inst real 0x%x \n",
         muse_context_array[id].run_layer_inst_len_real);
  printf("muse layer weight addr 0x%x \n",
         muse_context_array[id].wet_addr_offset);
  printf("muse layer act addr 0x%x \n", muse_context_array[id].act_addr_offset);
  printf("muse layer output addr 0x%x \n",
         muse_context_array[id].out_addr_offset);
  printf("muse layer run times %d \n", muse_context_array[id].run_layer_times);

  printf("================ MUSE REGS ================\n\n");

  printf("MUSE_REG_INST_BASE_ADDR 0x%x \n",
         muse_context_array[id].muse_reg_inst_base_addr);
  printf("MUSE_REG_ACT_BASE_ADDR0 0x%x \n",
         muse_context_array[id].muse_reg_act_base_addr0);
  printf("MUSE_REG_ACT_BASE_ADDR1 0x%x \n",
         muse_context_array[id].muse_reg_act_base_addr1);
  printf("MUSE_REG_WEIGHT_BASE_ADDR 0x%x \n",
         muse_context_array[id].muse_reg_weight_base_addr);
  printf("MUSE_REG_OUTPUT_BASE_ADDR 0x%x \n",
         muse_context_array[id].muse_reg_output_base_addr);
  printf("MUSE_REG_INST_LEN_REAL 0x%x \n",
         muse_context_array[id].muse_reg_inst_len_real);
  printf("MUSE_REG_INST_LEN_READ 0x%x \n",
         muse_context_array[id].muse_reg_inst_len_read);
  printf("MUSE_REG_CONFIG_END 0x%x \n",
         muse_context_array[id].muse_reg_config_end);
  printf("MUSE_REG_INTERRUPT_STATE 0x%x \n",
         muse_context_array[id].muse_reg_interrupt_state);
  printf("=========================================\n\n\n");
}

void show_muse_ctrl_info() {
  printf("=============  MUSE CONTRL INFO ============= \n");
  int i = 0;
  for (i = 0; i < MUSE_SUPPORT_MAX_NUMBER; ++i) {
    printf("id %d, status %d, ddr addr 0x%x \n", i,
           (int)(muse_contrl_info.muses_work_status[i]),
           muse_contrl_info.muses_ddr_addr[i]);
  }
}

int muse_check_layer_run_finished(int id) {
  uint32_t interrupt_status = get_muse_reg_value(
      muse_context_array[id].base_addr, MUSE_REG_INTERRUPT_STATE);
  if ((interrupt_status & wait_signal) != wait_signal) {
    return 0;
  }
  return 1;
}

void muse_task(int id) {
  uint8_t muse_status = muse_context_array[id].muse_status;
  struct timeval muse_end_time;
  unsigned long muse_used_time = 0;

  if (muse_status == 0x00)
    return;  // TASK_NONE
  else if (muse_status == 0x03)
    return;                                     // TASK_FINISHED
  else if (muse_status == 0x01) {               // LAYER_WAITING
    muse_context_array[id].muse_status = 0x02;  // TASK_RUNNING

    printf("\n************************************\n");
    printf("muse id %d run start layer %d... \n\n", id,
           muse_context_array[id].run_layer_number + 1);
    // show_muse_ctrl_info();
    // show_muse_context(id);
    muse_conf_regs(id);
    metal_gettimeofday(&(muse_start_time[id]), NULL);
    muse_read_regs(id);
    update_muse_context_info(id);
  } else if (muse_status == 0x02) {
    if (muse_check_layer_run_finished(id) == 0x01) {
      // show_muse_context(id);
      muse_context_array[id].muse_status = 0x03;  // TASK_FINISHED

      metal_gettimeofday(&muse_end_time, NULL);
      muse_used_time =
          (muse_end_time.tv_sec - muse_start_time[id].tv_sec) * 1000 * 1000 +
          (muse_end_time.tv_usec - muse_start_time[id].tv_usec);
      muse_used_time = muse_used_time * 8;
      muse_context_array[id].run_layer_times = muse_used_time;

      printf("muse id %d run finished layer %d! \n\n", id,
             muse_context_array[id].run_layer_number + 1);
      muse_read_regs(id);
      update_muse_context_info(id);
    }
  }
}

void muse_task_run() {
  get_muse_context_info();
  int i = 0;
  for (i = 0; i < MUSE_SUPPORT_MAX_NUMBER; ++i) {
    if (muse_contrl_info.muses_work_status[i] != 0x00) {
      muse_task(i);
    }
  }
}

void muse_run_loop() {
  init_muse_ctrl_info_ddr();

  while (1) {
    get_muse_ctrl_info();
    muse_task_run();
  }
}